#include "stdafx.h"
#define MYLIB_API_EXPORT
#include "CGAL_IO_OBJ.h"
#include <CGAL/IO/Polyhedron_iostream.h>

//initialize the static member
std::auto_ptr<Parser_obj> Parser_obj::_instance(0);

Builder_onesolid_obj::Builder_onesolid_obj(FILE *pFile, std::vector<simple_material_obj>* mtl, FILE *m_mFile, long num, bool isOutput)
{
	m_pFile = pFile;
	materiallist = mtl;
	m_midFile = m_mFile;
	m_pVtNum = num;
	bIsValid = true;
	bIsOutput = isOutput;
	bmtlFlag = true;
#ifdef _DEBUG_START_FROM_RANDOM_FACE_INDEX
	static_debug_flag = 1;
#endif //_DEBUG_START_FROM_RANDOM_FACE_INDEX
}

void Builder_onesolid_obj::operator()(HDS& hds)
{
	Builder builder(hds,true);
	builder.begin_surface(3,1,6);
	read_vertices(builder);
	if (!read_facets(builder))
	{
		return;
	}
	//
	if ( builder.check_unconnected_vertices()) 
	{
		if ( ! builder.remove_unconnected_vertices())
		{
			return;
		}
	}
	//
	builder.end_surface();
}

bool Builder_onesolid_obj::is_valid()
{
	return bIsValid;
}

void Builder_onesolid_obj::read_vertices(Builder &builder)
{
	char pLine[512];
	while(fgets(pLine,512,m_pFile))
	{
		if (strstr(pLine,"vn"))
		{
			//doing nothing
		}
		else if(pLine[0] == 'v')
		{
			//read
			float x,y,z;
			if(sscanf(pLine,"v %f %f %f",&x,&y,&z) == 3)
				builder.add_vertex(Point(x,y,z));
			//write to the mid file
			if (bIsOutput)
			{
				//write to the mid file
				fputs(pLine,m_midFile);
			}
		}
		if (strstr(pLine,"vertices"))
		{
			//write to the mid file
			if (bIsOutput)
			{
				//write to the mid file
				fputs(pLine,m_midFile);
			}
			break;
		}
	}
	if (bIsOutput)
	{
		fprintf(m_midFile,"\n",0);
	}
}

bool Builder_onesolid_obj::read_facets(Builder &builder)
{
	char pLine[512];
	if (!materiallist||materiallist->empty())
	{
		while(fgets(pLine,512,m_pFile))
		{
			if (strstr(pLine,"faces"))
			{
				if (bIsOutput)
				{
					//write to the mid file
					fputs(pLine,m_midFile);
					fprintf(m_midFile,"\n",0);
				}
				break;
			}
			else if (!add_facet(builder,pLine, false, std::string("")))
			{
				return false;
			}
		}
	}
	else 
	{
		std::string Mtl_name;
		while(fgets(pLine,512,m_pFile))
		{
			if (strstr(pLine,"usemtl"))
			{
				char mtltmp[80];
				if (sscanf(pLine, "usemtl %s\n",mtltmp) != 1)
				{
					// 						CGAL_assertion(0);
					// 						return false;
				}
				Mtl_name = std::string(mtltmp);
			}
			else if (strstr(pLine,"faces"))
			{
				if (bIsOutput)
				{
					//write to the mid file
					fputs(pLine,m_midFile);
					fprintf(m_midFile,"\n",0);
				}
				break;
			}
			else if(!add_facet(builder,pLine,true,Mtl_name))
			{
				return false;
			}
		}
	}//else if
	return true;
}


bool Builder_onesolid_obj::add_facet(Builder &builder,char* pLine,bool bWithmtl,std::string &Mtl_name)
{
	char *pTmp = pLine;
	if(pTmp[0] == 'f')
	{
		//n is the vertex normal(no use here)
		int index[3],n[3];
		char index_ascii[512],n_ascii[512];
		// facet geometry
		pTmp += 2; // jump after 'f '
		if(strstr(pTmp,"//"))
		{
			int temp = 0;
			while(sscanf(pTmp,"%d//%d",&index[temp],&n[temp]))
			{
				//
				_itoa_s(index[temp],index_ascii,10);
				_itoa_s(n[temp],n_ascii,10);
				//for absolute vertices indexing
				index[temp] -= m_pVtNum;
				//
				pTmp += (2 +
					strlen(index_ascii) + strlen(n_ascii));
				if(strlen(pTmp) < 3)
					break;
				else
					pTmp += 1;
				//
				++temp;
			}
		}
		else if(strstr(pTmp,"/"))
		{
			int temp = 0;
			while(sscanf(pTmp,"%d/%d",&index[temp],&n[temp]))
			{
				_itoa_s(index[temp],index_ascii,10);
				_itoa_s(n[temp],n_ascii,10);
				//for absolute vertices indexing
				index[temp] -= m_pVtNum;
				pTmp += (1 +
					strlen(index_ascii) + strlen(n_ascii));
				if(strlen(pTmp) < 3)
					break;
				else
					pTmp += 1;
				//
				++temp;
			}
		}
		else
		{
			int temp = 0;
			while(sscanf(pTmp,"%d",&index[temp]))
			{
				_itoa_s(index[temp],index_ascii,10);
				//for absolute vertices indexing
				index[temp] -= m_pVtNum;
				pTmp += strlen(index_ascii);
				if(strlen(pTmp) < 3)
					break;
				else
					pTmp += 1;
				++temp;
			}
		}
		//actually add face and exclude degeneration
		if (index[0] != index[1]&&index[0] != index[2]&&index[2] != index[1])
		{
			//try to add the facet into HDS and tag the invalid facet in the mid file
			add(builder,index,bWithmtl,Mtl_name);
			//write to the mid file
			if (bIsOutput)
			{
				//write to the mid file
				fprintf(m_midFile,"f %d %d %d\n",index[0],index[1],index[2]);
			}
		}
		else
		{
			CGAL_assertion(0);
		}
	}//if

	return true;
}


void Builder_onesolid_obj::add(Builder &builder, int* index, bool bWithmtl, std::string Mtl_name)
{
#ifdef _DEBUG_START_FROM_RANDOM_FACE_INDEX
	//for debug purpose
	if (static_debug_flag == 1&&((index[0]>1&&index[1]>1&&index[2]>1)||
		((index[0] == 0||index[1] == 0||index[2] == 0))))
	{
		int imin = std::min(index[0],index[1]);
		imin = std::min(imin,index[2]);
		if (imin !=0)
		{//started from larger index than 1
			m_pVtNum = imin-1;
			index[0] -= m_pVtNum;
			index[1] -= m_pVtNum;
			index[2] -= m_pVtNum;
		}
		else
		{//started from 0
			m_pVtNum = -1;
			index[0] -= m_pVtNum;
			index[1] -= m_pVtNum;
			index[2] -= m_pVtNum;
		}
		static_debug_flag = 0;
	}
#endif //_DEBUG_START_FROM_RANDOM_FACE_INDEX
	//add vertices to face
	std::vector< std::size_t> t;
	t.push_back(index[0]-1);
	t.push_back(index[1]-1);
	t.push_back(index[2]-1);

	//for test
	/*if (index[0] == 6 && index[1] == 7 && index[2] == 8)
	{
	int ttetest= 88;
	}*/
	//test manifold
	if (builder.test_facet(t.begin(),t.end()) == true)
	{
		//test degeneration
		Polyhedron_3::Plane_3 plane(builder.vertex(t[0])->point(), 
			builder.vertex(t[1])->point(), 
			builder.vertex(t[2])->point() 
			); 
		double a = plane.a();
		double b = plane.b();
		double c = plane.c();
		//if (!plane.is_degenterate())
		if (fabs(a) > DEGENERATION_TEST || fabs(b) > DEGENERATION_TEST || fabs(c) > DEGENERATION_TEST )
		{
			// create facet
			Face_handle f = builder.begin_facet();
			if (bWithmtl)
			{
				// facet material
				std::vector<simple_material_obj>::iterator itr;
				for (itr = materiallist->begin(); itr != materiallist->end(); ++itr)
				{
					if ((*itr).get_name() == Mtl_name)
					{
						f->mtl = *itr;
						break;
					}
				}
			}
			builder.add_vertex_to_facet(t[0]);
			builder.add_vertex_to_facet(t[1]);
			builder.add_vertex_to_facet(t[2]);
			builder.end_facet();

			//
			if (bmtlFlag)//we assume the first face is always manifold (of course)
			{
				if (bIsOutput)
				{
					//write to the mid file
					fprintf(m_midFile,LOGMTLNAME_VALID,0);
				}
				bmtlFlag = false;
			}
		}
		else
		{
			//non manifold faces
			if (!bmtlFlag)
			{
				if (bIsOutput )
				{
					//write to the mid file
					fprintf(m_midFile,LOGMTLNAME_INVALID,0);
				}
				bmtlFlag = true;
			}
			//
			bIsValid = false;
		}
	}
	else
	{
		//the last chance
		/*t.clear();
		t.push_back(index[0]-1); t.push_back(index[2]-1); t.push_back(index[1]-1);
		itr1 = t.begin();
		itr2 = t.end();
		if (builder.test_facet(itr1,itr2) == true)
		{
		int tmp = index[1];
		index[1] = index[2];
		index[2] = tmp;
		add(builder, index, bWithmtl, Mtl_name);
		}
		else*/
		{
			//non manifold faces
			if (!bmtlFlag)
			{
				if (bIsOutput )
				{
					//write to the mid file
					fprintf(m_midFile,LOGMTLNAME_INVALID,0);
				}
				bmtlFlag = true;
			}
			//
			bIsValid = false;
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void Parser_obj::init()
{
	logVertxNum = 0;
	if (materiallist)
	{
		materiallist->clear();
		delete materiallist;
		materiallist = NULL;
	}
}

bool Parser_obj::read_model_obj(const char*pFilename,const char *mtlFilename, ModelOBJ* obj, bool bIsOutput /* = false */ )
{
	CGAL_assertion(obj!=NULL);
	//logfile
	std::string logname(pFilename);
	std::string::size_type idx = logname.find('.obj') - 3/*obj*/;
	std::string tmpname = logname.substr(0,idx);
	//
	FILE *plogFile = NULL;
	if (bIsOutput)
	{
		std::string tmpmtlname = tmpname+ "_log.mtl";
		tmpname += "_log.obj";
		plogFile = fopen(tmpname.c_str(),"w");
		if(plogFile == NULL)
		{
			CGAL_assertion(0);
			return false;
		}
		//mtllib in logfile
		fprintf(plogFile,"#Non - 2 manifold solids by Persim\n",0);
		std::string::size_type idxmtl = tmpmtlname.rfind('\\');
		std::string mtllibname = tmpmtlname.substr(idxmtl,idx + 8/*_log.obj*/);
		mtllibname = "mtllib ." + mtllibname + "\n";
		fprintf(plogFile,mtllibname.c_str(),0);
		//
		FILE* plogmtlFile = fopen(tmpmtlname.c_str(),"w");
		if(plogmtlFile == NULL)
		{
			CGAL_assertion(0);
			return false;
		}
		//write default mtl file
		fprintf(plogmtlFile,LOGMTLDEF_INVALID,0);
		fprintf(plogmtlFile," Ka %.4f %.4f %.4f\n",1.0,0.0,0.0);
		fprintf(plogmtlFile," Kd %.4f %.4f %.4f\n",1.0,0.0,0.0);
		fprintf(plogmtlFile," Ks %.4f %.4f %.4f\n",0.0,0.0,0.0);
		fprintf(plogmtlFile,LOGMTLDEF_VALID,0);
		fprintf(plogmtlFile," Ka %.4f %.4f %.4f\n",0.0,1.0,0.0);
		fprintf(plogmtlFile," Kd %.4f %.4f %.4f\n",0.0,1.0,0.0);
		fprintf(plogmtlFile," Ks %.4f %.4f %.4f\n",0.0,0.0,0.0);
		//fprintf(plogmtlFile,LOGMTLDEF_OPEN,0);
		//fprintf(plogmtlFile," Ka %.4f %.4f %.4f\n",0.0,0.0,1.0);
		//fprintf(plogmtlFile," Kd %.4f %.4f %.4f\n",0.0,0.0,1.0);
		//fprintf(plogmtlFile," Ks %.4f %.4f %.4f\n",0.0,0.0,0.0);
		fclose(plogmtlFile);
	}
	//obj
	FILE* pFile = fopen(pFilename,"r");
	if(pFile == NULL)
	{
		CGAL_assertion(0);
		return false;
	}
	//mtl
	FILE *mtlFile = fopen(mtlFilename, "r");
	if(mtlFile != NULL)
	{
		materiallist = new std::vector<simple_material_obj>;
		read_materials(mtlFile);
	}
	//
	std::vector<Polyhedron_3*>::iterator itr;
	long vtNum = 0;
	//
	while(1)
	{
		//
		bool bIsFinished = false;
		SolidOBJ* solid = new SolidOBJ;
		if (!read_one_solid_translate(pFile, plogFile, solid, vtNum,bIsOutput,bIsFinished))
		{
			//finished
			if (bIsFinished)
			{
				delete solid;
				solid = NULL;
				break;
			}
			else
			{
				//handlethe non-2-manifold solid
				vtNum += solid->Solid_mesh->size_of_vertices();
#ifdef _DEBUG_IKIP_NON2MANIFOLD
				delete solid;
				solid = NULL;
#else
				obj->push_back(solid);
#endif // _DEBUG_IKIP_NON2MANIFOLD
			}
		}
		else
		{
			//add for test
			std::transform(solid->Solid_mesh->facets_begin(),solid->Solid_mesh->facets_end(),solid->Solid_mesh->planes_begin(),Plane_equation());
			//add the manifold solid
			obj->push_back(solid);
			vtNum += solid->Solid_mesh->size_of_vertices();
		}
	}
	if (bIsOutput)
	{
		fclose(plogFile);
	}
	if (mtlFile)
	{
		fclose(mtlFile);
	}
	fclose(pFile);
	delete materiallist;
	materiallist = NULL;
	if (obj->empty())
	{
		CGAL_assertion(0);
		return false;
	}
	if (bIsOutput)
	{
		DeleteFile(MIDFILEPATH);
	}
	return true;
}

bool Parser_obj::write_obj(const char* pFilename, const ModelOBJ* obj, const bool bIsOpenOutput /* = false */ )
{
	FILE* fp = fopen(pFilename,"w");
	if (fp == NULL)
	{
		CGAL_assertion(0);
		return false;
	}
	//head
	fprintf(fp,"# OBJ file out put by PERSIM; plz rename mtl file as follows\n");
	fprintf(fp,"\n");
	//mtl
	std::string str(pFilename);
	std::string::size_type idx0 = str.rfind('\\');
	str = str.substr(idx0+1);
	std::string::size_type idx1 = str.find('.');
	str = str.substr(0,idx1 - 1);//...log2.mtl->...log.mtl
	str += ".mtl";
	fprintf(fp,"mtllib %s\n", str.c_str());//the same name as the obj file by default
	fprintf(fp,"\n");
	//mainbody
	int vtnum = 0;//for absolute vt index
	for (SOLID_const_itr itr = obj->begin();itr != obj->end();++itr)
	{
		write_solid(fp,*itr, vtnum, bIsOpenOutput);
	}
	fprintf(fp,"\n");
	fclose(fp);
	//
	return true;
}

//bool Parser_obj::convert_hds_renderable(const ModelOBJ* input, Renderable_OBJ* output)
//{
//	CGAL_assertion(output!=NULL||input != NULL);
//	ModelOBJ::const_iterator itr;
//	//for each solid
//	for (itr = input->begin(); itr != input->end(); ++itr)
//	{
//		Polyhedron_3* cur = (*itr)->Solid_mesh;
//		//firstly add all the vertices(for bbox)
//		Vertex_itr vt_itr;
//		for (vt_itr = cur->vertices_begin(); vt_itr != cur->vertices_end(); ++vt_itr)
//		{
//			output->Vertices.push_back(Point_3((*vt_itr).point().x(),(*vt_itr).point().y(),(*vt_itr).point().z()));
//		}
//		//calculate face normal
//		std::transform(cur->facets_begin(),cur->facets_end(),cur->planes_begin(),Plane_equation());
//		//then group face
//		Face_itr face_itr;
//		for (face_itr = cur->facets_begin(); face_itr != cur->facets_end(); ++face_itr)
//		{
//			//check its material, whether exists in output
//			bool bfound = false;
//			MTLFACETS_itr mtl_itr;
//			for (mtl_itr = output->Mtlfacets.begin(); mtl_itr != output->Mtlfacets.end(); ++mtl_itr)
//			{
//				if ((*face_itr).mtl.get_name() == (*mtl_itr)->mtl.get_name())
//				{
//					bfound = true;
//					break;
//				}
//			}
//			//add facet
//			if (bfound)
//			{//exists. then add to the MTLFACETS
//				(*mtl_itr)->faces.push_back(*face_itr);
//			} 
//			else
//			{//does not exist. add a new MTLFACETS
//				MTLFACETS* newmtlface = new MTLFACETS;
//				//material
//				newmtlface->mtl = (*face_itr).mtl;
//				newmtlface->faces.push_back((*face_itr));
//				//add mtlfacet
//				output->Mtlfacets.push_back(newmtlface);
//			}
//		}			
//	}
//	return true;
//}

void Parser_obj::convert_to_off(const SolidOBJ* solid, const char* pFilename)
{
	FILE* outfile = NULL;
	outfile = fopen(pFilename,"w");
	if (outfile == NULL)
	{
		return;
	}
	//title
	fprintf(outfile,"OFF\n","");
	//number of vertices and facets
	Polyhedron_3 poly = *(solid->Solid_mesh);
	//
	fprintf(outfile,"%d %d %d\n", poly.size_of_vertices(), poly.size_of_facets(), 0);
	//write vertices
	Vertex_itr vt_itr = poly.vertices_begin();
	while(vt_itr != poly.vertices_end())
	{
		Polyhedron_3::Vertex vt = *vt_itr++;
		//inexact kernal
		fprintf(outfile,"%lf %lf %lf\n", vt.point().x(),vt.point().y(), vt.point().z());
	}
	//write facets
	Face_itr fc_itr = poly.facets_begin();
	while(fc_itr != poly.facets_end())
	{
		Polyhedron_3::Face fc = *fc_itr++;
		Polyhedron_3::Halfedge_around_facet_circulator hitr = fc.facet_begin();
		CGAL_assertion(CGAL::circulator_size(hitr) ==3 );//triangle
		fprintf(outfile,"3 %d ", std::distance(poly.vertices_begin(),hitr->vertex()));
		++hitr;
		fprintf(outfile,"%d ", std::distance(poly.vertices_begin(),hitr->vertex()));
		++hitr;
		fprintf(outfile,"%d \n", std::distance(poly.vertices_begin(),hitr->vertex()));
	}
	//
	fclose(outfile);
}

void Parser_obj::convert_off_to_obj(const char* pOffname, const char* pObjname)
{
	ModelOBJ result;
	SolidOBJ* solid = new SolidOBJ;
	solid->Solid_name = "OFF Solid";
	std::fstream file;
	file.open(pOffname);
	if (!file.is_open())
	{
		return;
	}
	file>>*(solid->Solid_mesh);
	file.close();
	result.push_back(solid);
	write_obj(pObjname,&result);
	delete solid;
	solid = NULL;
}

void Parser_obj::vary_mtl_for_obj(const char* pObjname, const char* pClrname, material_prop& mtlprop)
{
	//read the clr file to mtllib
	std::vector<simple_material_obj> mtllib;//record mtl
	if (!read_clr_to_mtl(pClrname, mtlprop, mtllib))
	{
		std::cerr<<"error:read_clr_to_mtl"<<std::endl;
		return;
	}
	//create a mtl file
	if (!write_mtl_to_file(pObjname, mtlprop, mtllib))
	{
		std::cerr<<"error:write_mtl_to_file"<<std::endl;
		return;
	}
	//read the obj file and write it with the mtllib
	if (!vary_mtl_in_obj(pObjname, mtllib))
	{
		std::cerr<<"error:vary_mtl_in_obj"<<std::endl;
		return;
	}
}

bool Parser_obj::vary_mtl_in_obj(const char* pObjname, std::vector<simple_material_obj>& mtllib)
{
	//the name of newobj
	std::string objname(pObjname);
	std::string::size_type idx = objname.rfind('.');
	std::string newname = objname.substr(0,idx)+"_vm";
	std::string newobjname = newname + ".obj";;

	//
	std::fstream fileori, filenew;
	fileori.open(objname);
	if (!fileori.is_open())
	{
		return false;
	}
	filenew.open(newobjname, std::ios::out);
	if (!filenew.is_open())
	{
		return false;
	}
	//update the value for mtllib and usemtl
	std::vector<simple_material_obj>::iterator mtl_itr = mtllib.begin();
	while (1)
	{
		//read
		std::string aline;
		std::getline(fileori,aline);
		//update the mtl
		int idx = aline.find("mtllib");//mtllib
		if (-1 != idx)
		{
			int pathindex = newname.rfind("\\");
			newname = newname.substr(pathindex+1, newname.length());
			aline = "mtllib " + newname + ".mtl";
		}
		idx = aline.find("usemtl");//usemtl
		if (-1 != idx)
		{
			aline = "usemtl " + mtl_itr->get_name();
			if(++mtl_itr == mtllib.end())
			{
				mtl_itr = mtllib.begin();
			}
		}
		//
		if (fileori.eof())
		{
			break;
		}
		//write
		filenew<<aline<<std::endl;
	}
	//
	fileori.close();
	filenew.close();
	return true;
}

bool Parser_obj::write_mtl_to_file(const char* pObjname, material_prop& mtlprop, std::vector<simple_material_obj>& mtllib)
{
	//the name of new mtl
	std::string objname(pObjname);
	std::string::size_type idx = objname.rfind('.');
	std::string mtlname = objname.substr(0,idx);
	mtlname += "_vm.mtl";//refer to the explaination of the call function
	//write mtl to the file
	std::fstream file;
	file.open(mtlname, std::ios::out);
	if (!file.is_open())
	{
		return false;
	}
	//write
	file<<"# Persim: for representing different models in a file"<<std::endl;
	std::vector<simple_material_obj>::iterator mtl_itr = mtllib.begin();
	while(mtl_itr != mtllib.end())
	{
		simple_material_obj mtl = *mtl_itr++;
		file.precision(6);
		file<<std::endl;
		file<<"newmtl "<<mtl.get_name().c_str()<<std::endl;
		file<<"  Ns "<<mtlprop.get_Ns()<<std::endl;
		file<<"  Ni "<<mtlprop.get_Ni()<<std::endl;
		file<<"  d "<<mtlprop.get_d()<<std::endl;
		file<<"  Tr "<<mtlprop.get_Tr()<<std::endl;
		file<<"  Tf "<<mtlprop.get_Tf()[0]<<" "<<mtlprop.get_Tf()[1]<<" "<<mtlprop.get_Tf()[2]<<std::endl;
		file<<"  illum "<<mtlprop.get_illum()<<std::endl;
		file<<"  Ka "<<mtl.get_ambient()[0]/255<<" "<<mtl.get_ambient()[1]/255<<" "<<mtl.get_ambient()[2]/255<<std::endl;
		file<<"  Kd "<<mtl.get_diffuse()[0]/255<<" "<<mtl.get_diffuse()[1]/255<<" "<<mtl.get_diffuse()[2]/255<<std::endl;
		file<<"  Ks "<<mtl.get_specular()[0]/255<<" "<<mtl.get_specular()[1]/255<<" "<<mtl.get_specular()[2]/255<<std::endl;
		file<<"  Ke "<<0.0<<" "<<0.0<<" "<<0.0<<std::endl;
	}
	//
	file.close();
	return true;
}

bool Parser_obj::read_clr_to_mtl(const char* pClrname, material_prop& mtlprop, std::vector<simple_material_obj>& mtllib)
{
	std::fstream file;
	file.open(pClrname);
	if (!file.is_open())
	{
		return false;
	}
	char tmpchar[40];
	int tmpint;
	file>>tmpchar>>tmpint>>tmpint;
	int Mtl_id = 0;
	//
	while (1)
	{
		double tmp;
		int dr, dg, db;
		//read
		file>>tmp>>dr>>dg>>db;
		//
		if (file.eof())
		{
			break;
		}
		//
		char Id_char[50];
		_itoa(Mtl_id++,Id_char,10);
		std::string Id_str (Id_char);
		simple_material_obj onemtl(Id_str
			,dr, dg, db
			,dr, dg, db
			,mtlprop.get_Ks()[0], mtlprop.get_Ks()[1], mtlprop.get_Ks()[2]);
		mtllib.push_back(onemtl);
	}
	//
	file.close();
	return true;
}

bool Parser_obj::read_one_solid_translate(FILE* pFile, FILE* plogFile, SolidOBJ* solid, long pVtnum, bool isOutput, bool& isFinished)
{
	//logfile
	FILE* m_midFp = NULL;
	if(isOutput)
	{
		m_midFp = fopen(MIDFILEPATH,"w");
		if (m_midFp == NULL)
		{
			CGAL_assertion(0);
			return false;
		}
	}

	//read solid name
	if (!read_solid_name(pFile,m_midFp,solid,isOutput))
	{
		//finished
		isFinished = true;
		return false;
	}

	//read solid mesh and validate the added facet
	Builder_onesolid_obj builder(pFile, materiallist,m_midFp,pVtnum,isOutput); 
	solid->Solid_mesh->delegate(builder);//call the function object of the builder

	//finished writing mid file
	if(isOutput)
	{
		fclose(m_midFp);
	}	

	//output the open non-2-manifold solid globally
	if (!builder.is_valid())
	{
		if(isOutput)
		{
			m_midFp = fopen(MIDFILEPATH,"r");
			//write the solid to log file
			char pLine[512];
			int tmp = 0;
			while(fgets(pLine,512,m_midFp))
			{
				//convert face indices from local to global
				if (strstr(pLine,"vertices"))//current vertices number
				{
					if (sscanf(pLine,"# %d vertices", &tmp) != 1)
					{
						CGAL_assertion(0);
						return false;
					}
				}
				if (pLine[0] == 'f')
				{
					int index[3];
					if (sscanf(pLine,"f %d %d %d\n",&index[0],&index[1],&index[2]) == 3)
					{
						fprintf(plogFile,"f %d %d %d\n", index[0]+ logVertxNum,index[1]+ logVertxNum, index[2]+ logVertxNum);
					}
					else
					{
						CGAL_assertion(0);
						return false;
					}
				}
				else
				{
					fputs(pLine, plogFile);
				}
			}
			//update the vertex index
			logVertxNum += tmp;
			//
			fclose(m_midFp);
		}
		return false;
	}
	return true;
}

bool Parser_obj::read_solid_name(FILE* pFile,FILE* pMidFile,SolidOBJ* solid, bool isOutput)
{
	CGAL_assertion(solid!=NULL);
	char pLine[512];
	while(fgets(pLine,512,pFile))
	{
		if (strstr(pLine,"# object"))
		{
			if (isOutput)
			{
				//write to the mid file
				fputs(pLine,pMidFile);
			}				
			char name[80];
			if (sscanf(pLine,"# object %s", &name) == 1)
			{
				solid->Solid_name = std::string(name);
				return true;
			}
			else if (sscanf(pLine,"# object %s to come ...", &name) == 1)
			{
				solid->Solid_name = std::string(name);
				return true;
			}
			else
			{
				solid->Solid_name = std::string("#");
				return true;
			}
		}
	}
	return false;
}

bool Parser_obj::read_materials(FILE* m_mtlFile)
{
	fseek(m_mtlFile,0,SEEK_SET);
	char pLine[512];
	while(fgets(pLine,512,m_mtlFile))
	{
		simple_material_obj mtl;
		if (strstr(pLine, "newmtl"))
		{
			std::string temp;
			char mtltmp[80];
			if (sscanf(pLine,"newmtl %s\n",mtltmp) != 1)
			{
				CGAL_assertion(0);
				return false;
			}
			mtl.set_name(std::string(mtltmp));
			//
			int tag = 0;
			while(fgets(pLine,512,m_mtlFile))
			{
				float num0, num1, num2;
				if (strstr(pLine, "Ka"))
				{
					if (sscanf(pLine,"Ka  %f %f %f\n",&num0,&num1,&num2) == 3)
					{
						mtl.set_ambient(num0, num1, num2);
						tag++;
					}
					else if (sscanf(pLine," Ka  %f %f %f\n",&num0,&num1,&num2) == 3)
					{
						mtl.set_ambient(num0, num1, num2);
						tag++;
					}
					else
					{
						CGAL_assertion(0);
						return false;
					}
				}
				else if (strstr(pLine, "Kd"))
				{
					if (sscanf(pLine,"Kd  %f %f %f\n",&num0,&num1,&num2) == 3)
					{
						mtl.set_diffuse(num0, num1, num2);
						tag++;
					}
					else if (sscanf(pLine," Kd  %f %f %f\n",&num0,&num1,&num2) == 3)
					{
						mtl.set_diffuse(num0, num1, num2);
						tag++;
					}
					else
					{
						CGAL_assertion(0);
						return false;
					}
				}
				else if (strstr(pLine, "Ks"))
				{
					if (sscanf(pLine,"Ks  %f %f %f\n",&num0,&num1,&num2) == 3)
					{
						mtl.set_specular(num0, num1, num2);
						tag++;
					}
					else if (sscanf(pLine," Ks  %f %f %f\n",&num0,&num1,&num2) == 3)
					{
						mtl.set_specular(num0, num1, num2);
						tag++;
					}
					else
					{
						CGAL_assertion(0);
						return false;
					}
				}
				if(tag == 3)
				{
					break;
				}
			}
			//
			materiallist->push_back(mtl);
		}
	}
	return true;
}

void Parser_obj::write_solid(FILE* fp, SolidOBJ* solid, int& vtnum, bool bIsOpenOutput )
{
	fprintf(fp,"#\n");
	fprintf(fp,"# object %s\n", solid->Solid_name.c_str());
	fprintf(fp,"#\n");
	fprintf(fp,"\n");
	//begin vertices
	write_vertices(fp, solid);
	//number of vertices
	fprintf(fp,"# %d vertices\n", solid->Solid_mesh->size_of_vertices());
	//end vertices
	fprintf(fp,"\n");
	fprintf(fp,"g %s\n", solid->Solid_name.c_str());
	//begin faces with mtl
	write_faces(fp, solid, vtnum, bIsOpenOutput);
	//number of faces
	fprintf(fp,"# %d faces\n", solid->Solid_mesh->size_of_facets());
	//end faces
	fprintf(fp,"\n");

	vtnum += solid->Solid_mesh->size_of_vertices();
}

void Parser_obj::write_vertices(FILE* fp, SolidOBJ* solid)
{
	Polyhedron_3::Vertex_iterator itr;
	for (itr = solid->Solid_mesh->vertices_begin(); itr != solid->Solid_mesh->vertices_end(); ++itr)
	{
		Polyhedron_3::Vertex vt= *itr;
		fprintf(fp,"v  %.8lf %.8lf %.8lf\n", vt.point().x(), vt.point().y(), vt.point().z());
	}
}

void Parser_obj::write_faces(FILE* fp, SolidOBJ* solid, int& vtnum, bool bIsOpenOutput )
{
	simple_material_obj tempmtl;
	bool tag = false;
	Polyhedron_3::Face_iterator itr;

	for (itr = solid->Solid_mesh->facets_begin(); itr != solid->Solid_mesh->facets_end(); ++itr)
	{
		Polyhedron_3::Facet fc = *itr;
		//Check whether the face has a border
		if (bIsOpenOutput)
		{
			Polyhedron_3::Halfedge_around_facet_circulator hitr = fc.facet_begin();
			CGAL_assertion(CGAL::circulator_size(hitr) == 3 );//triangle
			bool bIsOpen = false;
			do 
			{
				if (hitr->is_border()||hitr->opposite()->is_border())
				{
					bIsOpen = true;
				}
			} while (++hitr!=fc.facet_begin());

			if (bIsOpen)
			{
				//has a border
				fprintf(fp,"%s", LOGMTLNAME_INVALID);
				//write vertex indices
				fprintf(fp,"f %d ", std::distance(solid->Solid_mesh->vertices_begin(),hitr->vertex())+vtnum+1);
				++hitr;
				fprintf(fp,"%d ", std::distance(solid->Solid_mesh->vertices_begin(),hitr->vertex())+vtnum+1);
				++hitr;
				fprintf(fp,"%d \n", std::distance(solid->Solid_mesh->vertices_begin(),hitr->vertex())+vtnum+1);

				//problematic
				fprintf(fp,"usemtl %s\n", "invalid");
				continue;
			}
		}
		//write face mtl
		if (!tag)
		{
			//first mtl
			tempmtl = fc.mtl;
			tag = true;
			if (tempmtl.get_ambient()[0]<1.0&&tempmtl.get_ambient()[0]>0.0)
			{
				fprintf(fp,"usemtl %s\n", tempmtl.get_name().c_str());
			}
			else
			{
				fprintf(fp,"usemtl %s\n", "invalid");
			}
		}
		else
		{
			//last mtls
			if (tempmtl.get_name() != fc.mtl.get_name())
			{
				tempmtl = fc.mtl;
				fprintf(fp,"usemtl %s\n", tempmtl.get_name().c_str());
			}
		}
		//write vertex indices
		Polyhedron_3::Halfedge_around_facet_circulator hitr = fc.facet_begin();
		CGAL_assertion(CGAL::circulator_size(hitr) == 3 );//triangle
		fprintf(fp,"f %d ", std::distance(solid->Solid_mesh->vertices_begin(),hitr->vertex())+vtnum+1);
		++hitr;
		fprintf(fp,"%d ", std::distance(solid->Solid_mesh->vertices_begin(),hitr->vertex())+vtnum+1);
		++hitr;
		fprintf(fp,"%d \n", std::distance(solid->Solid_mesh->vertices_begin(),hitr->vertex())+vtnum+1);
	}
}